package com.zenithmind.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zenithmind.user.mapper.VerificationCodeMapper;
import com.zenithmind.user.pojo.domain.VerificationCode;
import com.zenithmind.user.service.EmailService;
import com.zenithmind.user.service.VerificationCodeService;
import com.zenithmind.common.result.Result;
import com.zenithmind.common.result.ResultCode;
import com.zenithmind.user.mapper.UserMapper;
import com.zenithmind.user.pojo.domain.User;
import com.zenithmind.user.pojo.dto.VerificationRequestDTO;
import com.zenithmind.user.enums.VerificationCodeType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zenithmind.common.result.PageResult;
import com.zenithmind.user.pojo.query.VerificationCodeQuery;
import com.zenithmind.user.pojo.vo.VerificationCodeVO;
import org.springframework.beans.BeanUtils;
import java.util.stream.Collectors;
import com.zenithmind.common.annotation.DataCache;
import com.zenithmind.common.annotation.DataCacheEvict;

/**
 * 验证码服务实现
 */
@Slf4j
@Service
public class VerificationCodeServiceImpl extends ServiceImpl<VerificationCodeMapper, VerificationCode> implements VerificationCodeService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private EmailService emailService;
    
    @Autowired
    private VerificationCodeMapper verificationCodeMapper;
    
    @Autowired
    private UserMapper userMapper;

    @Value("${verification.code.expire-minutes:5}")
    private int expireMinutes;

    // Redis key前缀
    private static final String CODE_KEY_PREFIX = "verification:code:";
    
    // 冷却时间（1分钟）
    private static final long CODE_RESEND_COOLDOWN_SECONDS = 60;

    @Override
    @DataCacheEvict(prefix = "verification:code", allEntries = true)
    public void generateCode(VerificationRequestDTO verificationRequestDTO) {
        // 根据DTO类型设置不同的验证码行为
        if (verificationRequestDTO.getType() != null && 
            verificationRequestDTO.getType() == VerificationCodeType.REGISTRATION.getValue()) {
            // 对于注册类型的验证码，邮箱是必需的
            if (StringUtils.isBlank(verificationRequestDTO.getEmail())) {
                log.error("生成注册验证码失败: 邮箱为空");
                return;
            }
            
            // 如果未提供用户名，使用默认的注册前缀+邮箱作为用户名
            String email = verificationRequestDTO.getEmail();
            String username = verificationRequestDTO.getUsername();
            if (StringUtils.isBlank(username)) {
                username = "register_" + email;
            }
            
            // 使用邮箱和用户名生成注册验证码
            generateRegistrationCode(email, username, verificationRequestDTO.getIpAddress());
        } else {
            // 其他类型使用用户名和邮箱
            generateCode(verificationRequestDTO.getUsername(), verificationRequestDTO.getEmail(), verificationRequestDTO.getIpAddress());
        }
    }

    @Override
    @DataCacheEvict(prefix = "verification:code", allEntries = true)
    public void generateCode(String username, String email, String ipAddress) {
        // 生成6位随机数字验证码
        String code = generateSixDigitCode();
        
        // 存储验证码到Redis，设置过期时间
        String redisKey = getRedisKey(username);
        redisTemplate.opsForValue().set(redisKey, code, expireMinutes, TimeUnit.MINUTES);
        
        // 存储验证码到数据库
        VerificationCode verificationCode = new VerificationCode();
        verificationCode.setUsername(username);
        verificationCode.setEmail(email);
        verificationCode.setCode(code);
        verificationCode.setType(0); // 默认登录验证码
        verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(expireMinutes));
        verificationCode.setVerified(0); // 未验证
        verificationCode.setIpAddress(ipAddress);
        verificationCodeMapper.insert(verificationCode);
        
        // 发送验证码到邮箱
        emailService.sendVerificationCode(email, code);
        
        log.info("生成验证码成功: {}, 邮箱: {}, IP: {}", username, maskEmail(email), ipAddress);
    }

    @Override
    @DataCacheEvict(prefix = "verification:code", allEntries = true)
    public void generateCode(String username, String email) {
        // 默认不传IP地址的实现
        generateCode(username, email, "unknown");
    }

    @Override
    public Result<Void> sendLoginVerificationCode(VerificationRequestDTO verificationRequestDTO) {
        return sendLoginVerificationCode(verificationRequestDTO.getUsername());
    }

    @Override
    public Result<Void> sendLoginVerificationCode(String username) {
        if (StringUtils.isBlank(username)) {
            return Result.fail(ResultCode.PARAM_ERROR);
        }

        // --- 验证码冷却时间检查 ---
        String redisKey = CODE_KEY_PREFIX + username;
        Long ttl = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS);
        long totalExpireSeconds = TimeUnit.MINUTES.toSeconds(expireMinutes);

        // 如果验证码存在且未过期，并且距离过期时间小于冷却时间，则返回错误
        if (ttl != null && ttl > 0 && ttl > (totalExpireSeconds - CODE_RESEND_COOLDOWN_SECONDS)) {
            log.warn("用户 {} 请求验证码过于频繁", username);
            // Use TOO_MANY_REQUESTS code
            return Result.fail(ResultCode.TOO_MANY_REQUESTS.getCode(), "请求过于频繁，请稍后 (" + (ttl - (totalExpireSeconds - CODE_RESEND_COOLDOWN_SECONDS)) + "秒) 再试", null);
        }
        // --- Cooldown Check END ---

        // 查询用户信息
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        User user = userMapper.selectOne(queryWrapper);
        
        if (user == null) {
            // 为安全起见，不向前端泄露用户不存在的信息，但记录日志
            log.warn("尝试为不存在的用户 {} 发送验证码", username);
            return Result.success(); // 静默成功
        }

        // 检查用户邮箱
        if (StringUtils.isBlank(user.getEmail())) {
            log.warn("用户 {} 邮箱未设置，无法发送验证码", username);
            return Result.fail(ResultCode.EMAIL_REQUIRED);
        }

        // 生成验证码并发送
        try {
            // generateCode internally handles setting the code in Redis with TTL
            generateCode(username, user.getEmail());
            log.info("已为用户 {} 发送登录验证码", username);
            return Result.success();
        } catch (Exception e) {
            log.error("为用户 {} 发送验证码失败: {}", username, e.getMessage(), e);
            // 返回通用错误，避免泄露过多信息
            return Result.fail(ResultCode.SYSTEM_ERROR.getCode(), "验证码发送失败", null);
        }
    }

    @Override
    @DataCacheEvict(prefix = "verification:code", key = "#verificationRequestDTO.username", 
                  beforeInvocation = false, allEntries = false)
    public boolean verifyCode(VerificationRequestDTO verificationRequestDTO) {
        // For registration, we might not have a username yet, but we should have an email and type
        if (verificationRequestDTO.getType() != null && 
            verificationRequestDTO.getType() == VerificationCodeType.REGISTRATION.getValue() &&
            StringUtils.isNotBlank(verificationRequestDTO.getEmail())) {
            return verifyCodeByEmail(verificationRequestDTO.getEmail(), verificationRequestDTO.getCode());
        }
        return verifyCode(verificationRequestDTO.getUsername(), verificationRequestDTO.getCode());
    }

    @Override
    @DataCacheEvict(prefix = "verification:code", key = "#username")
    public boolean verifyCode(String username, String code) {
        if (code == null || code.isEmpty()) {
            return false;
        }
        
        // 从Redis验证
        String redisKey = getRedisKey(username);
        String storedCode = redisTemplate.opsForValue().get(redisKey);
        
        boolean isValid = storedCode != null && storedCode.equals(code);
        
        if (isValid) {
            // 验证成功后删除Redis中的验证码
            redisTemplate.delete(redisKey);
            
            // 更新数据库中的验证码状态
            LambdaUpdateWrapper<VerificationCode> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(VerificationCode::getUsername, username)
                      .eq(VerificationCode::getCode, code)
                      .eq(VerificationCode::getVerified, 0) // 未验证的
                      .set(VerificationCode::getVerified, 1) // 设置为已验证
                      .set(VerificationCode::getVerifiedTime, LocalDateTime.now());
            verificationCodeMapper.update(null, updateWrapper);
            
            log.info("验证码验证成功: {}", username);
            return true;
        }
        
        log.warn("验证码验证失败: {}", username);
        return false;
    }
    
    /**
     * 验证邮箱验证码（用于注册流程）
     * 
     * @param email 邮箱地址
     * @param code 验证码
     * @return 验证结果
     */
    @DataCacheEvict(prefix = "verification:code", key = "'register:' + #email")
    public boolean verifyCodeByEmail(String email, String code) {
        if (StringUtils.isBlank(email) || StringUtils.isBlank(code)) {
            return false;
        }
        
        // 首先检查Redis中是否存在此验证码
        String redisKey = CODE_KEY_PREFIX + "register:" + email;
        String storedCode = redisTemplate.opsForValue().get(redisKey);
        
        boolean isValid = storedCode != null && storedCode.equals(code);
        
        if (isValid) {
            // 验证成功后删除Redis中的验证码
            redisTemplate.delete(redisKey);
            
            // 从数据库查询和更新最新的未验证的验证码
            LambdaQueryWrapper<VerificationCode> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(VerificationCode::getEmail, email)
                    // 不再限制固定的用户名格式，而是更宽泛地查询
                    .eq(VerificationCode::getCode, code)
                    .eq(VerificationCode::getType, VerificationCodeType.REGISTRATION.getValue())
                    .eq(VerificationCode::getVerified, 0) // 未验证
                    .orderByDesc(VerificationCode::getCreateTime)
                    .last("LIMIT 1");
                    
            VerificationCode verificationCode = verificationCodeMapper.selectOne(queryWrapper);
            
            if (verificationCode != null) {
                // 更新验证码状态为已验证
                LambdaUpdateWrapper<VerificationCode> updateWrapper = new LambdaUpdateWrapper<>();
                updateWrapper.eq(VerificationCode::getId, verificationCode.getId())
                        .set(VerificationCode::getVerified, 1) // 设置为已验证
                        .set(VerificationCode::getVerifiedTime, LocalDateTime.now());
                        
                verificationCodeMapper.update(null, updateWrapper);
                
                log.info("邮箱验证码验证成功: 邮箱={}", maskEmail(email));
                return true;
            }
        }
        
        log.warn("邮箱验证码验证失败: 邮箱={}, 验证码不存在或已过期", maskEmail(email));
        return false;
    }
    
    @Override
    @DataCache(prefix = "verification:history", key = "#username + ':' + #limit")
    public List<VerificationCode> getUserVerificationHistory(VerificationRequestDTO verificationRequestDTO) {
        return getUserVerificationHistory(verificationRequestDTO.getUsername(), verificationRequestDTO.getLimit());
    }
    
    @Override
    @DataCache(prefix = "verification:history", key = "#username + ':' + #limit")
    public List<VerificationCode> getUserVerificationHistory(String username, int limit) {
        LambdaQueryWrapper<VerificationCode> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VerificationCode::getUsername, username)
                    .orderByDesc(VerificationCode::getCreateTime)
                    .last("LIMIT " + limit);
        return verificationCodeMapper.selectList(queryWrapper);
    }
    
    @Override
    @DataCacheEvict(prefix = "verification:code", allEntries = true)
    public int cleanExpiredCodes() {
        LambdaQueryWrapper<VerificationCode> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.lt(VerificationCode::getExpireTime, LocalDateTime.now())
                    .eq(VerificationCode::getVerified, 0); // 只删除未验证的过期验证码
        
        List<VerificationCode> expiredCodes = verificationCodeMapper.selectList(queryWrapper);
        if (expiredCodes.isEmpty()) {
            return 0;
        }
        
        verificationCodeMapper.delete(queryWrapper);
        log.info("清理过期验证码 {} 条", expiredCodes.size());
        return expiredCodes.size();
    }
    
    @Override
    @DataCacheEvict(prefix = "verification:code", key = "#verificationRequestDTO.username")
    public boolean setCodeType(VerificationRequestDTO verificationRequestDTO) {
        return setCodeType(verificationRequestDTO.getUsername(), verificationRequestDTO.getType());
    }
    
    @Override
    @DataCacheEvict(prefix = "verification:code", key = "#username")
    public boolean setCodeType(String username, Integer type) {
        if (type == null || type < 0 || type > 2) {
            return false;
        }
        
        // 查找最新未验证的验证码
        LambdaQueryWrapper<VerificationCode> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VerificationCode::getUsername, username)
                    .eq(VerificationCode::getVerified, 0)
                    .orderByDesc(VerificationCode::getCreateTime)
                    .last("LIMIT 1");
        
        VerificationCode latestCode = verificationCodeMapper.selectOne(queryWrapper);
        if (latestCode == null) {
            return false;
        }
        
        LambdaUpdateWrapper<VerificationCode> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(VerificationCode::getId, latestCode.getId())
                    .set(VerificationCode::getType, type);
        
        return verificationCodeMapper.update(null, updateWrapper) > 0;
    }

    /**
     * 生成6位随机数字验证码
     */
    private String generateSixDigitCode() {
        Random random = new Random();
        int code = 100000 + random.nextInt(900000); // 生成100000-999999之间的随机数
        return String.valueOf(code);
    }

    /**
     * 获取Redis中存储验证码的key
     */
    private String getRedisKey(String username) {
        return CODE_KEY_PREFIX + username;
    }
    
    /**
     * 对邮箱进行掩码处理，用于日志记录
     */
    public String maskEmail(String email) {
        if (email == null || email.length() < 5 || !email.contains("@")) {
            return "invalid-email";
        }
        
        int atIndex = email.indexOf('@');
        int length = atIndex;
        String prefix = length <= 3 ? email.substring(0, 1) : email.substring(0, 3);
        String suffix = email.substring(atIndex);
        
        StringBuilder masked = new StringBuilder(prefix);
        for (int i = 0; i < length - prefix.length(); i++) {
            masked.append('*');
        }
        masked.append(suffix);
        
        return masked.toString();
    }

    /**
     * 获取客户端IP地址
     *
     * @param request HTTP请求
     * @return IP地址
     */
    public String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 设置请求DTO的IP地址
     *
     * @param verificationRequestDTO 验证码请求DTO
     * @param request HTTP请求
     */
    public void setRequestIpAddress(VerificationRequestDTO verificationRequestDTO, HttpServletRequest request) {
        verificationRequestDTO.setIpAddress(getClientIp(request));
    }

    /**
     * 生成验证码（带请求对象）
     *
     * @param verificationRequestDTO 验证码请求DTO
     * @param request HTTP请求
     */
    @DataCacheEvict(prefix = "verification:code", allEntries = true)
    public void generateCode(VerificationRequestDTO verificationRequestDTO, HttpServletRequest request) {
        // 设置IP地址
        setRequestIpAddress(verificationRequestDTO, request);
        generateCode(verificationRequestDTO);
    }

    /**
     * 分页查询验证码记录
     *
     * @param verificationCodeQuery 验证码查询条件，包含分页信息和查询条件
     * @return 分页验证码记录
     */
    @Override
    @DataCache(prefix = "verification:page", key = "#verificationCodeQuery.toString()", expireTime = 1800)
    public PageResult<VerificationCodeVO> pageVerificationCodes(VerificationCodeQuery verificationCodeQuery) {
        Page<VerificationCode> page = verificationCodeQuery.toPage();
        LambdaQueryWrapper<VerificationCode> wrapper = new LambdaQueryWrapper<>();
        
        // 添加查询条件
        wrapper.like(StringUtils.isNotBlank(verificationCodeQuery.getEmail()), 
                    VerificationCode::getEmail, verificationCodeQuery.getEmail())
               .orderByDesc(VerificationCode::getCreateTime);
        
        Page<VerificationCode> codePage = page(page, wrapper);
        
        // 转换为VO对象
        List<VerificationCodeVO> codeVOList = codePage.getRecords().stream().map(code -> {
            VerificationCodeVO codeVO = new VerificationCodeVO();
            BeanUtils.copyProperties(code, codeVO);
            return codeVO;
        }).collect(Collectors.toList());
        
        return new PageResult<>(codeVOList, codePage.getTotal(), codePage.getCurrent(), codePage.getSize());
    }
    
    /**
     * 统计验证码使用情况
     *
     * @return 统计数据
     */
    @Override
    @DataCache(prefix = "verification:stats", expireTime = 1800) // 30分钟缓存过期
    public Map<String, Object> getVerificationStatistics() {
        Map<String, Object> statistics = new HashMap<>();
        
        // 统计总验证码数
        long totalCount = count();
        statistics.put("totalCount", totalCount);
        
        // 统计已验证数
        LambdaQueryWrapper<VerificationCode> verifiedWrapper = new LambdaQueryWrapper<>();
        verifiedWrapper.eq(VerificationCode::getVerified, 1);
        long verifiedCount = count(verifiedWrapper);
        statistics.put("verifiedCount", verifiedCount);
        
        // 统计未验证数
        LambdaQueryWrapper<VerificationCode> unverifiedWrapper = new LambdaQueryWrapper<>();
        unverifiedWrapper.eq(VerificationCode::getVerified, 0);
        long unverifiedCount = count(unverifiedWrapper);
        statistics.put("unverifiedCount", unverifiedCount);
        
        // 计算验证率
        double verifiedRate = totalCount > 0 ? (double) verifiedCount / totalCount * 100 : 0;
        statistics.put("verifiedRate", Math.round(verifiedRate * 100) / 100.0);
        
        // 统计不同类型验证码
        Map<Integer, Long> typeCountMap = new HashMap<>();
        LambdaQueryWrapper<VerificationCode> typeWrapper = new LambdaQueryWrapper<>();
        typeWrapper.select(VerificationCode::getType)
                  .groupBy(VerificationCode::getType);
        List<Map<String, Object>> typeCounts = listMaps(typeWrapper);
        
        for (Map<String, Object> item : typeCounts) {
            Integer type = null;
            Object typeObj = item.get("type");
            
            if (typeObj instanceof Boolean) {
                type = (Boolean) typeObj ? 1 : 0;
            } else if (typeObj instanceof Integer) {
                type = (Integer) typeObj;
            } else if (typeObj instanceof Long) {
                type = ((Long) typeObj).intValue();
            }
            
            if (type != null) {
                // 使用 count 查询获取每种类型的数量
                LambdaQueryWrapper<VerificationCode> countWrapper = new LambdaQueryWrapper<>();
                countWrapper.eq(VerificationCode::getType, type);
                long count = count(countWrapper);
                typeCountMap.put(type, count);
            }
        }
        statistics.put("typeDistribution", typeCountMap);
        
        // 今日验证码统计
        LocalDateTime today = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        LambdaQueryWrapper<VerificationCode> todayWrapper = new LambdaQueryWrapper<>();
        todayWrapper.ge(VerificationCode::getCreateTime, today);
        long todayCount = count(todayWrapper);
        statistics.put("todayCount", todayCount);
        
        // 有效验证码数量
        LambdaQueryWrapper<VerificationCode> validWrapper = new LambdaQueryWrapper<>();
        validWrapper.ge(VerificationCode::getExpireTime, LocalDateTime.now())
                    .eq(VerificationCode::getVerified, 0);
        long validCount = count(validWrapper);
        statistics.put("validCount", validCount);
        
        return statistics;
    }

    @Override
    @DataCacheEvict(prefix = "verification:code", allEntries = true)
    public VerificationCode addOrUpdateVerificationCode(VerificationCode verificationCode) {
    
        if (StringUtils.isBlank(verificationCode.getId())) {
            save(verificationCode);
        } else {
            updateById(verificationCode);
        }
        return getById(verificationCode.getId());
    }

    /**
     * 生成注册验证码
     * 
     * @param email 邮箱地址
     * @param username 用户名（为数据库提供，验证码类型为注册时必须）
     * @param ipAddress IP地址
     */
    @DataCacheEvict(prefix = "verification:code", allEntries = true)
    private void generateRegistrationCode(String email, String username, String ipAddress) {
        // 生成6位随机数字验证码
        String code = generateSixDigitCode();
        
        // 存储验证码到Redis，使用邮箱作为键
        String redisKey = CODE_KEY_PREFIX + "register:" + email;
        redisTemplate.opsForValue().set(redisKey, code, expireMinutes, TimeUnit.MINUTES);
        
        // 存储验证码到数据库
        VerificationCode verificationCode = new VerificationCode();
        verificationCode.setEmail(email);
        verificationCode.setUsername(username);
        verificationCode.setCode(code);
        verificationCode.setType(VerificationCodeType.REGISTRATION.getValue()); // 注册验证码
        verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(expireMinutes));
        verificationCode.setVerified(0); // 未验证
        verificationCode.setIpAddress(ipAddress);
        verificationCodeMapper.insert(verificationCode);
        
        // 发送验证码到邮箱，使用注册类型(1)
        emailService.sendVerificationCode(email, code, VerificationCodeType.REGISTRATION.getValue());
        
        log.info("生成注册验证码成功: 邮箱={}, IP={}", maskEmail(email), ipAddress);
    }

}